#include "lock.h"
#include "time.h"
#include "stdio.h"
#include "sched.h"
#include "queue.h"
#include "screen.h"
#include "irq.h"

pcb_t pcb[NUM_MAX_TASK];

/* current running task PCB */
pcb_t *current_running;

/* global process id */
pid_t process_id = 1;


/* kernel stack ^_^ */
#define NUM_KERNEL_STACK 20

static uint64_t kernel_stack[NUM_KERNEL_STACK];
static int kernel_stack_count;

static uint64_t user_stack[NUM_KERNEL_STACK];
static int user_stack_count;

void init_stack()
{
     
}

uint64_t new_kernel_stack()
{
     
}

uint64_t new_user_stack()
{
     
}

static void free_kernel_stack(uint64_t stack_addr)
{
     
}

static void free_user_stack(uint64_t stack_addr)
{
     
}

/* Process Control Block */
void set_pcb(pid_t pid, pcb_t *pcb, task_info_t *task_info)
{
     
}

/* ready queue to run */
queue_t ready_queue ;

/* block queue to wait */
queue_t block_queue ;

//sleep queue to sleep
queue_t sleep_queue;

static void check_sleeping()
{
    pcb_t *p, *q;
    uint32_t current_time;
    p = sleep_queue.head;
    current_time = get_timer();
    while (p)
    {
        if(current_time - p->begin_sleep_time >= p->sleep_time){
            p->sleep_time = 0;
            p->status = TASK_READY;
            q = queue_remove(&sleep_queue, p);
            queue_push(&ready_queue, p);
            p = q;
        }
        else {
            p = p->next;
        }
    }
    
}

void scheduler(void)
{
    current_running->cursor_x = screen_cursor_x;
    current_running->cursor_y = screen_cursor_y;
    check_sleeping();
    if(current_running->status != TASK_BLOCKED && current_running->status != TASK_EXITED){
        current_running->status = TASK_READY;
        if(current_running->pid != 0){
            queue_push(&ready_queue, current_running);
        }
    }
    // if(!queue_is_empty(&ready_queue)){
    //     current_running = queue_dequeue(&ready_queue);
    // }
    do
    {
        current_running = queue_dequeue(&ready_queue);
    } while (current_running->status == TASK_EXITED);
    
    current_running->status = TASK_RUNNING;
    
    screen_cursor_x = current_running->cursor_x;
    screen_cursor_y = current_running->cursor_y;
    // if(current_running->first_entry == 1){
    //     current_running->first_entry == 0;
    //     scheduler_first();
    // }
    // else
    // {
    //     scheduler_again();
    // }
    
    // int i;
    // for(i = 1; i <= 5; i++){
    //     vt100_move_cursor(1, 4 + i);
    //     printk("PCB[%d] ----> %d times", i, pcb[i].count);
    // }
}

void do_sleep(uint32_t sleep_time)
{
    current_running->status = TASK_BLOCKED;
    current_running->begin_sleep_time = get_timer();
    current_running->sleep_time = sleep_time;
    queue_push(&sleep_queue, current_running);
    do_scheduler();
}

void do_exit(void)
{
    kprintf("will exit\n");
    do_kill(current_running->pid);
    //current_running->status = TASK_EXITED;
    do_scheduler();
}

void do_block(queue_t *queue)
{
    current_running->status = TASK_BLOCKED;
    queue_push(queue, current_running);
    do_scheduler();
}

void do_unblock_one(queue_t *queue)
{
    pcb_t *unblock_task;
    unblock_task = queue_dequeue(queue);
    unblock_task->status = TASK_READY;
    queue_push(&ready_queue, unblock_task);
}

void do_unblock_all(queue_t *queue)
{
    pcb_t *unblock_task;
    while(!queue_is_empty(queue)){
        unblock_task = queue_dequeue(queue);
        if(unblock_task->status != TASK_EXITED){
            unblock_task->status = TASK_READY;
            queue_push(&ready_queue, unblock_task);
        } 
    }
}

int do_spawn(task_info_t *task)
{
    //kprintf("---test1----\n");
    int i = 0;
    while(pcb_init_list[i] && i < NUM_MAX_TASK){
        i++;
    }
    if(i == NUM_MAX_TASK){
        kprintf("too many pcb!\n");
    }
    //kprintf("---init pcb[%d]----\n", i);
    //kprintf("---test2----\n");
    pcb_init_list[i] = 1;
    pcb[i].pid = process_id++;
    pcb[i].type = task->type;
    pcb[i].first_entry = 1;
    pcb[i].status = TASK_READY;
    pcb[i].next = NULL;
    pcb[i].prev = NULL;
    //kprintf("---test3----\n");
    pcb[i].context_of_kernel.reg[31] = (uint64_t)return_to_user;
    //pcb[i].context_of_user.reg[31] = task->entry_point;

    pcb[i].context_of_kernel.cp0_epc = task->entry_point;
    pcb[i].context_of_user.cp0_epc = task->entry_point;

    pcb[i].context_of_kernel.cp0_status = 0x10008001;
    pcb[i].context_of_user.cp0_status = 0x10008001;

    pcb[i].context_of_kernel.reg[29] = pcb[i].kernel_stack_top;
    pcb[i].context_of_user.reg[29] = pcb[i].user_stack_top;
    //kprintf("---test4----\n");
    queue_push(&ready_queue, &pcb[i]);
    //kprintf("---test5----\n");
}

int do_kill(pid_t pid)
{
    int i, j;
    for(i = 0; i < NUM_MAX_TASK; i++){
        if(pcb[i].pid == pid){
            break;
        }
    }
    for(j = 0; j < MAXLOCK; j++){
        if(pid == mutex_lock_list[j].holder){
            do_mutex_lock_release(&mutex_lock_list[j]);
        }
    }
    do_unblock_all(&(pcb[i].block_queue));
    pcb[i].status = TASK_EXITED;
    //pcb_init_list[i] = 0;
}

int do_waitpid(pid_t pid)
{
    int i;
    for(i = 0; i < NUM_MAX_TASK; i++){
        if(pcb[i].pid == pid){
            break;
        }
    }
    current_running->status = TASK_BLOCKED;
    queue_push(&(pcb[i].block_queue), current_running);
    do_scheduler();
}

// process show
void do_process_show()
{

     
}

pid_t do_getpid()
{
     
}

void do_clear(void){
    screen_clear(0, SCREEN_HEIGHT);
    screen_move_cursor(0, SCREEN_HEIGHT / 2);
    kprintf("--------------COMMAND--------------\n");
}

void do_ps(void){
    kprintf("-----------PROCESS-----------\n");
    int i = 0;
    int j = 0;
    for(j = 0; j < process_id; j++){
        if(pcb[j].status == TASK_RUNNING){
            kprintf("[%d] PID = %d, STATUS = TASK_RUNING\n", i, pcb[j].pid);
            i++;
        }
        else if(pcb[j].status == TASK_BLOCKED && pcb[j].pid != 0){
            kprintf("[%d] PID = %d, STATUS = TASK_BLOCK\n", i, pcb[j].pid);
            i++;
        }
        else if(pcb[j].status == TASK_READY){
            kprintf("[%d] PID = %d, STATUS = TASK_READY\n", i, pcb[j].pid);
            i++;
        }
    }
}